import pandas as pd
import numpy as np
from jili.data import obj as objinfo
import datetime
from jili.core import load
from jili.core.printlog import print
def cross_df(k,args):
    a=args.get('a','close')
    b = args.get('b', 'close')
    outtype=args.get("outtype","-11")
    out=args.get('out', 'bs')
    if isinstance(a,int) or isinstance(a,float):
        k["tmp"] = a - k[b]
    elif isinstance(b,int) or isinstance(b,float):
        k["tmp"] = k[a] - b
    else:
        k["tmp"] = k[a] - k[b]
    k["tmp1"] = k["tmp"] - k["tmp"].diff(1)
    k[out] = np.nan
    if outtype=="1-1":
        k.loc[(k["tmp"] > 0) & (k["tmp1"] <= 0), out] = 1
        k.loc[(k["tmp"] < 0) & (k["tmp1"] >= 0), out] = -1
    elif outtype=="bs":
        k.loc[(k["tmp"] > 0) & (k["tmp1"] <= 0), out] = "b"
        k.loc[(k["tmp"] < 0) & (k["tmp1"] >= 0), out] = "s"
    elif outtype=="sb":
        k.loc[(k["tmp"] > 0) & (k["tmp1"] <= 0), out] = "s"
        k.loc[(k["tmp"] < 0) & (k["tmp1"] >= 0), out] = "b"
    else:
        k.loc[(k["tmp"] > 0) & (k["tmp1"] <= 0), out] = -1
        k.loc[(k["tmp"] < 0) & (k["tmp1"] >= 0), out] = 1
    return k
def overnight_df(k,args):
    """
    Args:
        k:
        arg:
        {
        overhours：间隔小时数：overtype
            有夜盘合约:夜盘 3天（36h）及以上为假期；2:2天及以上是周末，0:10~16个小时为夜盘时间；1:6-10小时为日盘收盘时间
            无夜盘合约：则 3天（36h）以上为假期；2天以上是周末 18~24位过夜

        out：输出字段定义
        }
    Returns:
    """
    dd0 = [(10, 16), (6, 10), (48, 36), (36, 360)]
    dt = args.get("overtype", 0)
    isnight = args.get("isnight", True)
    o = args.get("out", "bs")
    if o not in k.columns:
        k[o] = "h"
    if isnight:
        if dt == 0:
            a = pd.Timedelta(hours=6)
            k["delatime"] = -k["timekey"].diff(-1)
            k.loc[k['delatime'] >= a, o] = 'c'
        elif dt == 1:
            a1 = pd.Timedelta(hours=dd0[1][0])
            a2 = pd.Timedelta(hours=dd0[1][1])
            k["delatime"] = -k["timekey"].diff(-1)
            k.loc[(k['delatime'] >= a1) & (k['delatime'] < a2), o] = 'c'
            a = pd.Timedelta(hours=48)
            k["delatime"] = -k["timekey"].diff(-1)
            k.loc[k['delatime'] >= a, o] = 'c'
        elif dt == 2:
            a = pd.Timedelta(hours=48)
            k["delatime"] = -k["timekey"].diff(-1)
            k.loc[k['delatime'] >= a, o] = 'c'
        else:
            a = pd.Timedelta(hours=36)
            #k[o] = "h"
            k["delatime"] = -k["timekey"].diff(-1)
            k.loc[k['delatime'] >= a, o] = 'c'
    else:
        if dt == 1:
            a = pd.Timedelta(hours=18)
        elif dt == 2:
            a = pd.Timedelta(hours=48)
        else:
            a = pd.Timedelta(hours=36)
        k["delatime"] = -k["timekey"].diff(-1)
        k.loc[k['delatime'] >= a, o] = 'c'
    return k
class pos_simple:
    def __init__(self):
        self.long=None
        self.short=None
        self.fx=None
        self.orderbs=[]
    def onorder(self,order,bs="bs",tkey="timekey",bsprice="close"):
        _bs=order[bs]
        if self.fx=="long":
            if _bs =="s":
                self.long.update({"closetimekey":order[tkey],"sellprice":order[bsprice]})
                self.orderbs.append(self.long)
                self.long=None
                self.fx = "short"
                self.short = {"fx": "short", "opentimekey": order[tkey], "sellprice": order[bsprice]}
            elif _bs in ["c","sc"]:
                self.long.update({"closetimekey": order[tkey], "sellprice": order[bsprice]})
                self.orderbs.append(self.long)
                self.long = None
                self.fx=None
        elif self.fx=="short":
            if _bs =="b":
                self.short.update({"closetimekey":order[tkey],"buyprice":order[bsprice]})
                self.orderbs.append(self.short)
                self.short=None
                self.fx = "long"
                self.long = {"fx": "long", "opentimekey": order[tkey], "buyprice": order[bsprice]}
            elif _bs in ["bc","c"]:
                self.short.update({"closetimekey": order[tkey], "buyprice": order[bsprice]})
                self.orderbs.append(self.short)
                self.short = None
                self.fx = None
        else:
            if _bs in ["b","bo"]:
                self.fx="long"
                self.long={"fx":"long","opentimekey":order[tkey],"buyprice":order[bsprice]}
            elif _bs in ["s","so"]:
                self.fx="short"
                self.short = {"fx": "short", "opentimekey": order[tkey], "sellprice": order[bsprice]}
    def get(self):
        return self.orderbs
class pos():
    def __init__(self,data=pd.DataFrame(),bs="bs",cost=0.1,holdmark=False):
        self.data = data
        self.cost=cost
        self.bs = bs
        #self.pos=None
        self.prebs=None
        self.ischeckbs=True
        self.holdmark=holdmark
        self.info={}
    def setdata(self,data):
        self.data=data
    def getobj(self):
        obj=None
        if not self.data.empty:
            if "obj" in self.data.columns:
                return self.data.iloc[0]["obj"]
        return obj
    def genbs(self):
        name="_"+self.bs
        if self.ischeckbs:
            while name in self.data.columns:
                name="_"+name
        return name
    def apply_check_rank_bs(self,x):
        if not pd.isna(x):
            if self.prebs:
                if self.prebs==x:
                    #print(self.prebs,x)
                    return np.nan
            self.prebs=x
        return x
    def performance_summary_11_bygraph(self,k,args):
        self.data = k
        self.bs=args.get("bs","bs")
        self.prebs = None
        self.ischeckbs=True
        isrevers=args.get("isrevers",False)
        return self.pl_11(isrevers)
    def performance_summary(self,isrevers=False):
        rst=self.pl(isrevers)
    def performance_summary_pos_bygraph(self,k,args):
        self.data = k
        self.bs=args.get("bs","bs")
        self.prebs = None
        self.ischeckbs=True
        isrevers=args.get("isrevers",False)
        ismaster=args.get("ismaster",True)
        pos = args.get("pos", "pos")
        return self.deal_pos(ismaster,isrevers,pos)
    def deal_pos(self,ismaster=True,isrevers=False,pos="pos"):
        obj = self.getobj()
        _bs = self.genbs()
        if self.ischeckbs:
            self.data[_bs] = self.data[self.bs].apply(self.apply_check_rank_bs)
            self.ischeckbs = False
        order = self.data.loc[~pd.isna(self.data[_bs])]
        if ismaster:
            if not self.info:
                self.info = load(r"F:\qjs\data\future_master_date.pkl")
            info=self.info[obj]
            t1=info[0]
            t2=info[1]+datetime.timedelta(days=1)
            order = order.loc[(order["timekey"]>=t1) &(order["timekey"]<=t2)]
        order = order.copy()
        order=order.sort_values(["timekey"])
        order=order.to_dict("records")
        poss=pos_simple()
        for i in order:
            poss.onorder(i)
        orders=poss.get()
        self.orders0=orders
        return self.calc_pos(self.data,orders,isrevers,pos)
    def calc_pos(self,k,orders,isrevers,pos="pos"):
        if isrevers:
            long=-1
            short=1
        else:
            long = 1
            short = -1
        for i in orders:
            if i["fx"]=="long":
                k.loc[(k["timekey"]>i["opentimekey"]) &(k["timekey"]<=i["closetimekey"]),pos]=long
            elif i["fx"]=="short":
                k.loc[(k["timekey"]>i["opentimekey"]) &(k["timekey"]<=i["closetimekey"]),pos]=short
        return k
    def performance_summary_bs_bygraph(self,k,args):
        self.data = k
        self.bs=args.get("bs","bs")
        self.prebs = None
        self.ischeckbs=True
        isrevers=args.get("isrevers",False)
        ismaster=args.get("ismaster",True)
        pos = args.get("pos", "pos")
        return self.pl_bs(ismaster,isrevers,pos)
    def pl_bs(self,ismaster=True,isrevers=False,pos="pos"):
        obj = self.getobj()
        _bs = self.genbs()
        if self.ischeckbs:
            self.data[_bs] = self.data[self.bs].apply(self.apply_check_rank_bs)
            self.ischeckbs = False
        order = self.data.loc[~pd.isna(self.data[_bs])]
        if ismaster:
            if not self.info:
                self.info = load(r"F:\qjs\data\future_master_date.pkl")
            info=self.info[obj]
            t1=info[0]
            t2=info[1]+datetime.timedelta(days=1)
            order = order.loc[(order["timekey"]>=t1) &(order["timekey"]<=t2)]
        order = order.copy()
        order=order.sort_values(["timekey"])
        order=order.to_dict("records")
        poss=pos_simple()
        for i in order:
            poss.onorder(i)
        orders=poss.get()
        self.calc_pos(self.data,orders,isrevers,pos)
        self.orders0=orders
        rst = {}
        rst["trad_num1"] = len(orders)
        rst["trad_num2"] = 2 * rst["trad_num1"]
        if orders:
            orders=pd.DataFrame(orders)
            orders["holdhours"]=orders["closetimekey"]-orders["opentimekey"]
            orders["holdhours"]=orders["holdhours"].apply(lambda x:x.total_seconds()/60/60)
            onetick = objinfo.getobjinfo(obj)["PriceTick"]
            orders["pl0"]=orders["sellprice"]-orders["buyprice"]
            if isrevers:
                orders["pl0"] = -orders["pl0"]
            orders["fee1"]=2 * objinfo.calcopenfee_tick(obj, orders["sellprice"])
            orders["pl1"] = orders["pl0"] - orders["fee1"]
            orders["fee2"]=orders["fee1"]+onetick * self.cost
            orders["pl2"] = orders["pl0"] - orders["fee2"]
            orders["pl0-revers"] = -orders["pl0"]
            orders["pl1-revers"] = orders["pl0-revers"] - 2 * objinfo.calcopenfee_tick(obj, orders["sellprice"])
            orders["pl2-revers"] = orders["pl1-revers"] - onetick * self.cost
            for i in ["pl0", "pl1", "pl2", "pl0-revers", "pl1-revers", "pl2-revers"]:
                rst[i] = orders[i].sum()
                ii = i + "-pre1"
                rst[ii] = rst[i] / rst["trad_num1"]
                iii = ii + "_ticks"
                rst[iii] = rst[ii] / onetick
                ii = i + "-pre2"
                rst[ii] = rst[i] / rst["trad_num2"]
            p = orders["sellprice"].mean()
            for i in ["fee1","fee2"]:
                rst[i] = orders[i].sum()
                ii=i+"%"
                rst[ii]=rst[i]/(p * objinfo.calclongmargin_pct(obj, p))
            rst["pl2%"] = rst["pl2"] / (p * objinfo.calclongmargin_pct(obj, p))
            rst["pl2-revers%"] = rst["pl2-revers"] / (p * objinfo.calclongmargin_pct(obj, p))
            rst["holdhours"]=[orders["holdhours"].min(),orders["holdhours"].quantile(.25),orders["holdhours"].quantile(.5),orders["holdhours"].quantile(.75),orders["holdhours"].max()]
        else:
            for i in ["pl0", "pl1", "pl2", "pl0-revers", "pl1-revers", "pl2-revers"]:
                rst[i] = 0
                ii = i + "-pre1"
                rst[ii] = 0
                iii = ii + "_ticks"
                rst[iii] = 0
                ii = i + "-pre2"
                rst[ii] = 0
            rst["pl2%"] = 0
            rst["pl2-revers%"] = 0
            rst["holdhours"] = [0,0,0,0,0]
        return rst
    def pl_11(self,isrevers=False):
        obj=self.getobj()
        _bs=self.genbs()
        if self.ischeckbs:
            self.data[_bs] = self.data[self.bs].apply(self.apply_check_rank_bs)
            self.ischeckbs = False
        k = self.data.loc[~pd.isna(self.data[_bs])]
        k=k.copy()
        rst = {}
        rst["trad_num1"] = len(k) - 1
        rst["trad_num2"] = 2 * rst["trad_num1"]
        if k.empty or rst["trad_num1"]==0:
            for i in ["pl0", "pl1", "pl2", "pl0-revers", "pl1-revers", "pl2-revers"]:
                rst[i] = 0
                ii = i + "-pre1"
                rst[ii] = 0
                iii = ii + "_ticks"
                rst[iii] = 0
                ii = i + "-pre2"
                rst[ii] = 0
        else:
            onetick=objinfo.getobjinfo(obj)["PriceTick"]
            if isrevers:
                k[self.bs]=-k[self.bs]
            k["pos_open"]=k["close"]*k[self.bs]
            k["pos_close"] = k["pos_open"]-k["pos_open"].diff(1)
            k["pl0"] = k["pos_open"] + k["pos_close"]
            k["pl1"] = k["pl0"]- 2 * objinfo.calcopenfee_tick(obj,k["close"])
            k["pl2"] = k["pl1"] - onetick*self.cost
            k["pl0-revers"] = -k["pl0"]
            k["pl1-revers"] = k["pl0-revers"] - 2 * objinfo.calcopenfee_tick(obj,k["close"])
            k["pl2-revers"] = k["pl1-revers"] - onetick * self.cost
            for i in ["pl0","pl1","pl2","pl0-revers","pl1-revers","pl2-revers"]:
                rst[i]=k[i].sum()
                ii=i+"-pre1"
                rst[ii] = rst[i]/rst["trad_num1"]
                iii = ii + "_ticks"
                rst[iii] = rst[ii] / onetick
                ii = i + "-pre2"
                rst[ii] = rst[i]/rst["trad_num2"]
        return rst
    def pl_11_detail(self,isrevers=False,ischeckbs=False):
        obj=self.getobj()
        _bs = self.genbs()
        if self.ischeckbs:
            self.data[_bs] = self.data[self.bs].apply(self.apply_check_rank_bs)
            self.ischeckbs = False
        k = self.data.loc[~pd.isna(self.data[_bs])]
        k=k.copy()
        rst = {}
        rst["trad_num1"] = len(k) - 1
        rst["trad_num2"] = 2 * rst["trad_num1"]
        if k.empty or rst["trad_num1"]==0:
            for i in ["pl0", "pl1", "pl2", "pl0-revers", "pl1-revers", "pl2-revers"]:
                rst[i] = 0
                ii = i + "-pre1"
                rst[ii] = 0
                iii = ii + "_ticks"
                rst[iii] = 0
                ii = i + "-pre2"
                rst[ii] = 0
            for i in ["pl0", "pl1", "pl2", "pl0-revers", "pl1-revers", "pl2-revers"]:
                rst[i + ">0"] = 0
                rst[i + "<0"] = 0
                rst[i + "=0"] = 0
        else:
            if isrevers:
                k[self.bs]=-k[self.bs]
            onetick = objinfo.getobjinfo(obj)["PriceTick"]
            k["pos_open"]=k["close"]*k[self.bs]
            k["pos_close"] = k["pos_open"]-k["pos_open"].diff(1)
            k["pl0"] = k["pos_open"] + k["pos_close"]
            k["pl1"] = k["pl0"] - 2 * objinfo.calcopenfee_tick(obj, k["close"])
            k["pl2"] = k["pl1"] -onetick * self.cost
            k["pl0-revers"] = -k["pl0"]
            k["pl1-revers"] = k["pl0-revers"] - 2 * objinfo.calcopenfee_tick(obj, k["close"])
            k["pl2-revers"] = k["pl1-revers"] -onetick * self.cost
            for i in ["pl0","pl1","pl2","pl0-revers","pl1-revers","pl2-revers"]:
                rst[i]=k[i].sum()
                ii=i+"-pre1"
                rst[ii] = rst[i]/rst["trad_num1"]
                iii = ii + "_ticks"
                rst[iii] = rst[ii] / onetick
                ii = i + "-pre2"
                rst[ii] = rst[i]/rst["trad_num2"]
            for i in ["pl0","pl1","pl2","pl0-revers","pl1-revers","pl2-revers"]:
                rst[i+">0"]=len(k[(k[i])>0])/rst["trad_num1"]
                rst[i+"<0"] = len(k[k[i] < 0]) / rst["trad_num1"]
                rst[i+"=0"] = len(k[k[i] == 0]) / rst["trad_num1"]
            k["pl2"].hist(bins=100)
            k.boxplot(["pl0", "pl1", "pl2"])
            k["pretimekey"] = k["timekey"].shift(1)
            k["difftime"] = (k["timekey"] - k["pretimekey"]).apply(lambda x: x.total_seconds() / 60)
            k["difftime"].hist(bins=100)
            k.boxplot(["difftime"])
            k["overnighttype"] = k.apply(self.overnighttype, axis=1)
            k["overnighttype"].hist()
            k1=k[k["overnighttype"]!=0]
            k1=k1.copy()
            k1["overnighttype"].hist()
            print(len(k1),k1["pl2"].sum(),k1["pl2"].mean())
        return rst,k
    def overnighttype(self,i):
        days=i["difftime"]/60/24
        if days<1:
            if i["pretimekey"].time()<datetime.time(15, 0) and i["timekey"].time()>datetime.time(15, 0):
                return 2
            elif i["pretimekey"].time()<datetime.time(23, 59,59) and i["timekey"].time()>datetime.time(9, 0):
                return 1
            elif i["pretimekey"].time()<datetime.time(3, 0) and i["timekey"].time()>datetime.time(9, 0):
                return 1
            else:
                return 0
        else:
            return 3

